package cmdutl

import (
	"bufio"
	"fmt"
	"gitee.com/xiakai001/goutl/errutl"
	"io"
	"os/exec"
	"strings"
	"sync"
)

func Exec(command string) error {

	args, err := parseCommandToArgs(command)
	if err != nil {
		return errutl.Wrap(err)
	}

	if len(args) == 0 {
		return errutl.New("args is empty, command=%s", command)
	}

	return ExecWithArgs(args[0], args[1:])
}

func ExecWithArgs(command string, args []string) error {

	cmd := exec.Command(command, args...)
	err := RunCommand(cmd)
	if err != nil {
		return errutl.Wrap(err)
	}

	return nil
}

func RunCommand(cmd *exec.Cmd) error {
	var wg sync.WaitGroup
	wg.Add(2)
	//捕获标准输出
	stdout, err := cmd.StdoutPipe()
	if err != nil {
		return errutl.Wrap(err)
	}
	readout := bufio.NewReader(stdout)
	go func() {
		defer wg.Done()
		printResult(readout)
	}()

	//捕获标准错误
	stderr, err := cmd.StderrPipe()
	if err != nil {
		return errutl.Wrap(err)
	}
	reader := bufio.NewReader(stderr)
	go func() {
		defer wg.Done()
		printResult(reader)
	}()

	//执行命令
	err = cmd.Run()
	if err != nil {
		return errutl.Wrap(err)
	}
	wg.Wait()
	return nil
}

func printResult(reader *bufio.Reader) {
	outputBytes := make([]byte, 200)
	for {
		n, err := reader.Read(outputBytes) //获取屏幕的实时输出(并不是按照回车分割，所以要结合sumOutput)
		if err != nil {
			if err == io.EOF {
				break
			}
			fmt.Println(err.Error())
		}
		output := string(outputBytes[:n])
		fmt.Print(output) //输出屏幕内容
	}

}

func parseCommandToArgs(command string) ([]string, error) {

	command = strings.TrimSpace(command)

	args := make([]string, 0, 30)
	i, start := 0, 0
	for i < len(command) {
		switch command[i] {
		case ' ':
			if i != start {
				args = append(args, strings.TrimSpace(command[start:i]))
			}
			start = i + 1
		case '"':
			if i != start {
				return nil, errutl.New("cmd_gen=%s split=%s", command, command[start:i])
			}
			i++
			start = i
			for i < len(command) {
				if command[i] == '"' {
					break
				}
				i++
			}
			if i >= len(command) {
				return nil, errutl.New("cmd_gen=%s split=%s", command, command[start:i])
			}
			args = append(args, strings.TrimSpace(command[start:i]))
			start = i + 1
		}
		i++
	}

	if start < i {
		args = append(args, command[start:i])
	}

	return args, nil
}
